/* Copyright 2014 Jan Wolter */

#include "stack.h"
#include "tableau.h"
#include "found.h"
#include "solve.h"
#include "dfs.h"
#include "game.h"

char *gamename= "Cruel";
char *gameopt= "WS";
Stack *gathered= NULL;
Stack *temp= NULL;

int nplayed= 0;
int nwon= 0;
int nabandoned= 0;
int cardsleft= 0;
int maxoff;
int baserank;
int dealflip;
int weirddeal= 0;
extern int redealOnlyWhenStuck;
int stuck= 1;

void setopt(char c)
{
    switch (c)
    {
	case 'W': weirddeal= 1; break;
	case 'S': stuck= 0; break;
    }
}

/* This is the main program for setting up the current game */
void exec(int gather, int arif, int over, int cut)
{
    int win;
    redealOnlyWhenStuck= stuck;

    /* Could set things like ndeck, nsuit here */

    /* Build and shuffle the deck */
    Stack *shuf= mixcards(arif, over, cut);

    /* Create the foundations */
    superfound= sf_acebase;
    for (int s= 0; s < nsuit; s++)
	makeFound(1, s, +1, 13);

    /* Deal one card to each foundation */
    for (int s= 0; s < nsuit; s++)
    {
	Card *c= extractCard(shuf, 1, -1);
	pushStack(stacks+foundation[nfound-nsuit+c->s].id, c);
    }

    /* Create the tableau - 12 piles of 4 faceup cards each */
    dealflip= FLIP_NONE;
    for (int i= 0; i < 12; i++)
	makeTableau(4, dealflip, shuf, S_SAMESUIT, -1, S_NONE, -1,
	    1, F_NONE, BREAK_ANYWHERE, 0);

    freeStack(shuf);

    /* Print initial state */
    if (verbose > 1) printState(stdout, stacks);

    maxoff= 0;
    win= solve(gather,0);
    extern int n_end_state;
    if (verbose > 0) printf("maxoff=%d redeals=%d\n",maxoff,n_end_state);
    nplayed++;
    cardsleft+= maxoff;
    if (win == 1)
	nwon++;
    else if (win == 2)
	nabandoned++;

    cleanFound();
    cleanTableau();
    cleanStacks();
    cleanVisited();
}

/* Our pickup/redeal algorithm */
void pickup_redeal(Stack *stks)
{
    Stack *deck= allocStack(ncard);
    if (weirddeal)
    {
	/* Erroneous Cruel Redeal */
	/* Gather the cards by columns, bottom to top */
	for (int k= 0; k < ntableau; k++)
	{
	    Stack *fs= stks+tableau[k].id;
	    for (int i= 0; i < fs->n; i++)
		pushStack(deck, fs->c[i]);

	    /* Empty the stack after we gather it */
	    fs->n= 0;
	}

	/* And deal them out again by columns, bottom to top */
	int ci= 0;
	for (int k= 0; ci < deck->n && k < ntableau; k++)
	{
	    int n= ((deck->n - ci) >= 4) ? 4 : (deck->n - ci);
	    for (int i= 0; i < n; i++)
	    {
		 Card *c= deck->c[ci++];
		 c->faceup= isfaceup(dealflip, i);
		 pushStack(stks+tableau[k].id, c);
	    }
	}
    }
    else
    {
	/* Standard Cruel deal */

	/* Gather the cards by columns, top to bottom */
	for (int k= 0; k < ntableau; k++)
	{
	    Stack *fs= stks+tableau[k].id;
	    for (int i= fs->n-1; i >= 0; i--)
		pushStack(deck, fs->c[i]);

	    /* Empty the stack after we gather it */
	    fs->n= 0;
	}

	/* And deal them out again by columns, top to bottom */
	int ci= 0;
	for (int k= 0; ci < deck->n && k < ntableau; k++)
	{
	    int n= ((deck->n - ci) >= 4) ? 4 : (deck->n - ci);
	    for (int i= 0; i < n; i++)
	    {
		 Card *c= deck->c[ci+n-1-i];
		 c->faceup= isfaceup(dealflip, i);
		 pushStack(stks+tableau[k].id, c);
	    }
	    ci+= n;
	}
    }

    freeStack(deck);
}


/* Gather up the cards into the global 'gathered' stack. This has to be
 * non-destructive, since we may not be done solving when it is called.
 */
void gatherAll()
{
    if (gathered == NULL)
	gathered= allocStack(ncard);
    else
	gathered->n= 0;

    gatherFound(gathered);
    gatherTableau(gathered);
    if (gathered->n != ncard)
    {
	printf("Gather Failed - found %d cards\n",gathered->n);
	exit(1);
    }
}

/* How good is a move?
 *
 * Returns 0 if the move is completely safe or manditory. Such moves will not
 * be marked as choice points for backtracking. Note that if a "redeal" move
 * is possible, then then no move is safe.
 *
 * For other moves, positive values are returned with lower values for better
 * moves.
 *
 * Negative values mean the move should not be done at all.
 *
 * So for Cruel:
 *
 *   0 = Any move to the foundation. (These are never really safe, since we
 *       always have redeals, but the software will notice that and treat them
 *       as unsafe. If we had a finite number of redeals, then they'd be safe
 *       after the final redeal.)
 *   0 = Any move onto stack of length 1
 *   0 = Any move onto a stack whose top two cards are already sorted.
 *   n = Any other card move where n = (14 - rank).
 *  20 = redeal ( see rateRedeal() )
 */
int rateMove(Stack *src, int index1, int len, Stack *dst)
{
    /* Moves to foundation are safish */
    if (dst->type == FOUND)
	return 0;

    /* Moves onto a singleton are safe - since spaces are unfillable, we'll
     * never want to move that card anyway, so covering it is OK. */
    if (dst->n <= 1)
	return 0;

    /* If the top two cards are already sorted, then we will never want
     * to move the top card, since it is already in the only place it
     * can be, so it is fine to bury it. */
    Card *c1= dst->c[dst->n-1];
    Card *c2= dst->c[dst->n-2];
    if (c1->s == c2->s && c1->n == c2->n+1)
	return 0;

    /* Otherwise, we prefer moving onto big cards  */
    return 14 - c1->n;
}

/* Can we redeal in the current state? If not, return -1. If so, return a move
 * rating for the redeal, comparible to those returned by rateMove().
 */
int rateRedeal()
{
    /* In cruel, redeals are always possible */
    return 20;
}

void printState(FILE *f, Stack *stks)
{
    printFound(f, stks);
    printTableau(f, stks);
}

int victory()
{
    int off= nOff();
    if (off > maxoff) maxoff= off;
    return (off == ncard);
}

/* Return a quality rating for the current solution. Bigger is better. Usually
 * it's just cards off. This is used to decide which solution to report in
 * cases where we never win.
 */
int quality()
{
    return nOff();
}

void summary()
{
    printf("Played:    %d games\n",nplayed);
    printf("Won:       %d games (%.2f%)\n",nwon,(float)nwon*100/nplayed);
    printf("Abandoned: %d games (%.2f%)\n",nabandoned,(float)nabandoned*100/nplayed);
    printf("Cards Off: %.2f\n",(float)cardsleft/nplayed);
}

/* Comparision function for stack order normalization in currstate() */
static int cmpstacks(const void *p1, const void *p2)
{
    const Stack *s1= *(const Stack **)p1;
    const Stack *s2= *(const Stack **)p2;
    if (s1->n == 0 || s2->n == 0)
	return s2->n - s1->n;
    int c1= s1->c[0] - deck;
    int c2= s2->c[0] - deck;
    return c2 - c1;
}

/* Return a string that uniquely describes the current state. */
char *currstate()
{
    /* ncards for all the cards on things
     * 12 for list terminators.
     */
    char buf[ncard+30], *p= buf;

    /* List of cards on each tableau pile - probably we should normalize
     * the order of tableau piles, sorting them by the first card on them.
     * That's not worth while for Cruel, since spaces aren't fillable.
     */
#ifdef SORT
    Stack *norm[12];
    for (int k= 0; k < ntableau; k++)
	norm[k]= stacks + tableau[k].id;
    /* This seg faults for some reason */
    qsort(norm, ntableau, sizeof(Stack *), cmpstacks);

    for (int k= 0; k < ntableau; k++)
    {
	Stack *s= norm[k];
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#else
    for (int k= 0; k < ntableau; k++)
    {
	Stack *s= stacks + tableau[k].id;
	for (int i= 0; i < s->n; i++)
	    *(p++)= (s->c[i] - deck) + 1;
	*(p++)= ncard + 1; /* The card list terminator */
    }
#endif
    *p= '\0';

    return strdup(buf);
}
